#pragma once

#include <muduo/net/TcpServer.h>
#include <muduo/net/EventLoop.h>
#include <muduo/net/TcpConnection.h>
#include <muduo/net/Buffer.h>
#include <muduo/base/CountDownLatch.h>
#include <muduo/net/EventLoopThread.h>
#include <muduo/net/TcpClient.h>
#include "detail.hpp"
#include "fields.hpp"
#include "abstract.hpp"
#include "message.hpp"
#include <mutex>
#include <unordered_map>

namespace bitrpc
{
    class MuduoBuffer : public BaseBuffer
    {
    public:
        using ptr = std::shared_ptr<MuduoBuffer>;
        MuduoBuffer(muduo::net::Buffer *buf) : _buf(buf) {}
        virtual size_t readableSize() override
        {
            DLOG("进来 virtual size_t readableSize() override")
            return _buf->readableBytes();
        }
        virtual int32_t peekInt32() override
        {
            // muduo库是一个网络库，从缓冲区取出一个4字节整形，会进行网络字节序的转换
            return _buf->peekInt32();
        }
        virtual void retrieveInt32() override
        {
            return _buf->retrieveInt32();
        }
        virtual int32_t readInt32() override
        {
            return _buf->readInt32();
        }
        virtual std::string retrieveAsString(size_t len) override
        {
            return _buf->retrieveAsString(len);
        }

    private:
        muduo::net::Buffer *_buf;
    };
    class BufferFactory
    {
    public:
        template <typename... Args>
        static BaseBuffer::ptr create(Args &&...args)
        {
            return std::make_shared<MuduoBuffer>(std::forward<Args>(args)...);
        }
    };

    class LVProtocol : public BaseProtocol /* 接收请求时用来对message进行解析和获得对应业务层的具象BaseMessage */
    {                                      /* 接收response时，用serialize对对应业务层的具象BaseMessage进行LV格式序列化*/
    public:
        //LV协议和http协议都可以很好的解决粘包问题
        //LV通过最前面的四字节len   http通过请求头中的content-length和空白行(空白行保证请求行请求头读完整，空白行和content一起保证body部分完整)
        // |--Len--|--VALUE--|
        // |--Len--|--mtype--|--idlen--|--id--|--body--|
        using ptr = std::shared_ptr<LVProtocol>;
        // 判断缓冲区中的数据量是否足够一条消息的处理
        virtual bool canProcessed(const BaseBuffer::ptr &buf) override
        {
            if (buf->readableSize() < lenFieldsLength) /* readableSize()表示获取当前缓冲区所有数据  */
            {                                          /* 如果数据量小于lenFieldsLength直接退出 */
                return false;
            }
            int32_t total_len = buf->peekInt32();
            // DLOG("total_len:%d", total_len);
            if (buf->readableSize() < (total_len + lenFieldsLength))
            {
                return false;
            }
            return true;
        }
        virtual bool onMessage(const BaseBuffer::ptr &buf, BaseMessage::ptr &msg) override /* 服务端接收处理消息 */
        {
            DLOG("调用protocol的onMessage模块")
            // 当调用onMessage的时候，默认认为缓冲区中的数据足够一条完整的消息
            int32_t total_len = buf->readInt32();  // 读取总长度
            MType mtype = (MType)buf->readInt32(); // 读取数据类型
            int32_t idlen = buf->readInt32();      // 读取id长度
            int32_t body_len = total_len - idlen - idlenFieldsLength - mtypeFieldsLength;
            std::string id = buf->retrieveAsString(idlen);
            std::string body = buf->retrieveAsString(body_len);
            msg = MessageFactory::create(mtype); /* 根据mtype来构造类型  */
            if (msg.get() == nullptr)
            {
                ELOG("消息类型错误，构造消息对象失败！");
                return false;
            }
            bool ret = msg->unserialize(body);
            if (ret == false)
            {
                ELOG("消息正文反序列化失败！");
                return false;
            }
            msg->setId(id);
            msg->setMType(mtype);
            return true;
        }
        virtual std::string serialize(const BaseMessage::ptr &msg) override /* 发送时的处理 */
        {
            // |--Len--|--mtype--|--idlen--|--id--|--body--|
            std::string body = msg->serialize();
            std::string id = msg->rid();
            auto mtype = htonl((int32_t)msg->mtype());
            int32_t idlen = htonl(id.size());
            int32_t h_total_len = mtypeFieldsLength + idlenFieldsLength + id.size() + body.size();
            int32_t n_total_len = htonl(h_total_len);
            // DLOG("h_total_len:%d", h_total_len);
            std::string result;
            result.reserve(h_total_len);
            result.append((char *)&n_total_len, lenFieldsLength);
            result.append((char *)&mtype, mtypeFieldsLength);
            result.append((char *)&idlen, idlenFieldsLength);
            result.append(id);
            result.append(body);
            // DLOG("serialize请求序列化成功");
            // DLOG("body: %s", body.c_str());
            // DLOG("id: %s", id.c_str());
            // DLOG("mtype: %d", (int)msg->mtype());
            // DLOG("result长度: %zu", result.size());
            // DLOG("result为: ");
            // for (char c : result)
            //{
            // DLOG("%02x ", (unsigned char)c);
            //}
            return result;
        }

    private:
        const size_t lenFieldsLength = 4;
        const size_t mtypeFieldsLength = 4;
        const size_t idlenFieldsLength = 4;
    };

    class TestProtocol : public BaseProtocol
    {
    public:
        // |--Len--|--VALUE--|
        // |--Len--|--mtype--|--idlen--|--id--|--body--|
        using ptr = std::shared_ptr<TestProtocol>();
        virtual bool canProcessed(const BaseBuffer::ptr &buf) /* 是否可以处理数据 */
        {
            // 肯定是一个LV格式的std::string
            if (buf->readableSize() < lenFieldsLength)
                return false;
            int len = buf->peekInt32(); // len代表整个长度
            if (buf->readableSize() < len + lenFieldsLength)
                return false;
            return true;
        }
        virtual bool onMessage(const BaseBuffer::ptr &buf, BaseMessage::ptr &msg) /* 在收到消息时进行解析 */
        {
            int32_t total_len = buf->readInt32(); // 读取总长度     类似前四个字节表示后面消息的长度
            MType mtype = (MType)buf->readInt32();
            int idlen = buf->readInt32();
            int32_t body_len = total_len - idlen - idlenFieldsLength - mtypeFieldsLength;
            std::string id = buf->retrieveAsString(idlen);
            std::string body = buf->retrieveAsString(body_len);
            // 将每个字段提取出来
            msg = MessageFactory::create(mtype); /* 根据mtype来构造类型  */
            if (msg.get() == nullptr)
            {
                ELOG("消息类型错误，构造消息对象失败！");
                return false;
            }
            bool ret = msg->unserialize(body);
            if (ret == false)
            {
                ELOG("消息正文反序列化失败！");
                return false;
            }
            msg->setId(id);
            msg->setMType(mtype);
            return true;
        }
        // |--Len--|--VALUE--|
        // |--Len--|--mtype--|--idlen--|--id--|--body--|
        virtual std::string serialize(const BaseMessage::ptr &msg) /*将msg序列化成std::string*/
        {
            MType mtype = msg->mtype();
            std::string id = msg->rid();
            int32_t idlen = id.size();
            std::string body = msg->serialize();
            int32_t bodylen = body.size();
            int32_t h_length = mtypeFieldsLength + idlenFieldsLength + idlen + bodylen;
            int32_t n_length = htons(h_length); /* 转成网络字节序 */
            int32_t id_nlen = htons(idlen);
            auto type = htons((int32_t)mtype);
            std::string result;
            result.reserve(h_length + lenFieldsLength);
            result.append((char *)&n_length, lenFieldsLength);
            result.append((char *)&mtype, mtypeFieldsLength);
            result.append((char *)&idlen, idlenFieldsLength);
            result.append(id);
            result.append(body);
        }

    private:
        const size_t lenFieldsLength = 4;
        const size_t mtypeFieldsLength = 4;
        const size_t idlenFieldsLength = 4;
    };

    class ProtocolFactory
    {
    public:
        template <typename... Args>
        static BaseProtocol::ptr create(Args &&...args)
        {
            return std::make_shared<LVProtocol>(std::forward<Args>(args)...);
        }
    };

    class MuduoConnection : public BaseConnection
    {
    public:
        using ptr = std::shared_ptr<MuduoConnection>;
        MuduoConnection(const muduo::net::TcpConnectionPtr &conn,
                        const BaseProtocol::ptr &protocol) : _protocol(protocol), _conn(conn) {}
        virtual void send(const BaseMessage::ptr &msg) override
        {
            DLOG("进来virtual void send(const BaseMessage::ptr &msg) override");
            std::string body = _protocol->serialize(msg);
            _conn->send(body);
        }
        virtual void shutdown() override
        {
            _conn->shutdown();
        }
        virtual bool connected() override
        {
            return _conn->connected();
        }

    private:
        BaseProtocol::ptr _protocol;
        muduo::net::TcpConnectionPtr _conn;
    };
    class ConnectionFactory
    {
    public:
        template <typename... Args>
        static BaseConnection::ptr create(Args &&...args)
        {
            return std::make_shared<MuduoConnection>(std::forward<Args>(args)...);
        }
    };

    class MuduoServer : public BaseServer
    {
    public:
        using ptr = std::shared_ptr<MuduoServer>;
        MuduoServer(int port) : _server(&_baseloop, muduo::net::InetAddress("0.0.0.0", port),
                                        "MuduoServer", muduo::net::TcpServer::kReusePort),
                                _protocol(ProtocolFactory::create()) {}
        virtual void start()
        {
            _server.setConnectionCallback(std::bind(&MuduoServer::onConnection, this, std::placeholders::_1));
            _server.setMessageCallback(std::bind(&MuduoServer::onMessage, this,
                                                 std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));
            _server.start();  // 先开始监听
            _baseloop.loop(); // 开始死循环事件监控
        }

    private:
        void onConnection(const muduo::net::TcpConnectionPtr &conn)
        { /* 判断当前是否还处于连接状态 */
            if (conn->connected())
            {
                std::cout << "连接建立！\n";
                auto muduo_conn = ConnectionFactory::create(conn, _protocol); /* Muduo库的连接进行二次封装，管理起来 */
                {
                    std::unique_lock<std::mutex> lock(_mutex);
                    _conns.insert(std::make_pair(conn, muduo_conn));
                }
                if (_cb_connection) /* 如果外部没有设置进_cb_connection，就不去调用 */
                    _cb_connection(muduo_conn);
            }
            else
            {
                std::cout << "连接断开！\n";
                BaseConnection::ptr muduo_conn;
                {
                    std::unique_lock<std::mutex> lock(_mutex);
                    auto it = _conns.find(conn);
                    if (it == _conns.end())
                    {
                        return;
                    }
                    muduo_conn = it->second;
                    _conns.erase(conn);
                }
                if (_cb_close)
                    _cb_close(muduo_conn); /* 如果外部没有设置进_cb_close，就不去调用 */
            }
        }
        /* 由数据到来就持续while循环进行处理当前请求 */
        void onMessage(const muduo::net::TcpConnectionPtr &conn, muduo::net::Buffer *buf, muduo::Timestamp)
        {
            DLOG("连接有数据到来，开始处理！");
            auto base_buf = BufferFactory::create(buf);
            while (1)
            {
                DLOG("有数据到来，进入循环！");
                if (_protocol->canProcessed(base_buf) == false) // 当前数据不可处理
                {
                    // 数据不足
                    if (base_buf->readableSize() > maxDataSize) /* 防止恶意攻击 一条不完整的请求占据太多缓冲区数据  */
                    {
                        DLOG("缓冲区中数据过大！");
                        conn->shutdown(); /* 关闭和客户端的连接 */
                        return;
                    }
                    DLOG("数据量不足！");
                    break; /* 数据量不足就退出，Reactor负责监控该conn */
                }
                DLOG("缓冲区中数据可处理！");
                BaseMessage::ptr msg;
                bool ret = _protocol->onMessage(base_buf, msg); /* protocol模块进行解析，此时这个msg已经设置成请求中的mtype了 */
                if (ret == false)
                {
                    conn->shutdown(); /* 关闭和客户端的连接 */
                    DLOG("缓冲区中数据错误！");
                    return;
                }
                // DLOG("消息反序列化成功！")
                BaseConnection::ptr base_conn; /* 在确定缓冲区的数据可以处理之后创建base_coon */
                {
                    std::unique_lock<std::mutex> lock(_mutex);
                    auto it = _conns.find(conn);
                    if (it == _conns.end())
                    {
                        conn->shutdown(); /* 关闭和客户端的连接 */
                        return;
                    }
                    base_conn = it->second;
                }
                DLOG("调用回调函数进行消息处理！");
                if (_cb_message)
                    _cb_message(base_conn, msg); /* 这里的_cb_message回调处理函数中传递base_coon的目的是在后面的模块出错时可以随时关闭连接吗 */
            }
        }

    private:
        const size_t maxDataSize = (1 << 16);
        BaseProtocol::ptr _protocol;
        muduo::net::EventLoop _baseloop;
        muduo::net::TcpServer _server;
        std::mutex _mutex;
        std::unordered_map<muduo::net::TcpConnectionPtr, BaseConnection::ptr> _conns;
    };
    class ServerFactory
    {
    public:
        template <typename... Args>
        static BaseServer::ptr create(Args &&...args)
        {
            return std::make_shared<MuduoServer>(std::forward<Args>(args)...);
        }
    };

    class MuduoClient : public BaseClient
    {
    public:
        using ptr = std::shared_ptr<MuduoClient>;
        MuduoClient(const std::string &sip, int sport) : _protocol(ProtocolFactory::create()),
                                                         _baseloop(_loopthread.startLoop()),
                                                         _downlatch(1),
                                                         _client(_baseloop, muduo::net::InetAddress(sip, sport), "MuduoClient") {}
        virtual void connect() override
        {
            DLOG("设置回调函数，连接服务器");
            _client.setConnectionCallback(std::bind(&MuduoClient::onConnection, this, std::placeholders::_1));
            DLOG("设置成功断开连接的回调，连接服务器");
            // 设置连接消息的回调
            _client.setMessageCallback(std::bind(&MuduoClient::onMessage, this,
                                                 std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));
            DLOG("设置成功收到消息的回调回调函数，连接服务器");
            // 连接服务器
            _client.connect(); /* 连接建立时回调onConnection */
            _downlatch.wait(); /* 等到_downlatch.countDown(); 开始loop循环*/
            DLOG("连接服务器成功！");
        }
        virtual void shutdown() override
        {
            return _client.disconnect();
        }
        virtual void send(const BaseMessage::ptr &msg) override
        {
            if (connected() == false)
            {
                ELOG("连接已断开！");
                return;
            }
            _conn->send(msg);
        }
        virtual BaseConnection::ptr connection() override
        {
            return _conn;
        }
        virtual bool connected()
        {
            return (_conn && _conn->connected());
        }

    private:
        void onConnection(const muduo::net::TcpConnectionPtr &conn) /* 在外部client->connect(),在内部自动会调用onConnection */
        {
            if (conn->connected())
            {
                std::cout << "连接建立！\n";
                _downlatch.countDown();                             // 计数--，为0时唤醒阻塞
                _conn = ConnectionFactory::create(conn, _protocol); /* 客户端的Baseconnection可以维护在成员变量中 */
            }
            else
            {
                std::cout << "连接断开！\n";
                _conn.reset();
            }
        }
        /* 有信息到来，自动调用onMessage*/
        void onMessage(const muduo::net::TcpConnectionPtr &conn, muduo::net::Buffer *buf, muduo::Timestamp)
        {
            DLOG("连接有数据到来，开始处理！");
            auto base_buf = BufferFactory::create(buf);
            while (1)
            {
                if (_protocol->canProcessed(base_buf) == false)
                {
                    // 数据不足
                    if (base_buf->readableSize() > maxDataSize)
                    {
                        conn->shutdown();
                        ELOG("缓冲区中数据过大！");
                        return;
                    }
                    // DLOG("数据量不足！");
                    break;
                }
                // DLOG("缓冲区中数据可处理！");
                BaseMessage::ptr msg;
                bool ret = _protocol->onMessage(base_buf, msg); /* 在内部msg会被请求的mtype定义 */
                if (ret == false)
                {
                    conn->shutdown();
                    ELOG("缓冲区中数据错误！");
                    return;
                }
                // DLOG("缓冲区中数据解析完毕，调用回调函数进行处理！");
                if (_cb_message) /* 由父类提供的Messagecallback()设置 */
                    _cb_message(_conn, msg);
            }
        }

    private:
        const size_t maxDataSize = (1 << 16);
        BaseProtocol::ptr _protocol;
        BaseConnection::ptr _conn; /* 本来是muduo::net::TcpConnectionPtr _conn; 这里我们用BaseConnection::ptr来代替*/
        muduo::CountDownLatch _downlatch;
        muduo::net::EventLoopThread _loopthread;
        muduo::net::EventLoop *_baseloop;
        muduo::net::TcpClient _client;
    };

    class ClientFactory
    {
    public:
        template <typename... Args>
        static BaseClient::ptr create(Args &&...args)
        {
            return std::make_shared<MuduoClient>(std::forward<Args>(args)...);
        }
    };
}

/*
除了BaseMessage的具象层的别的模块的具象层
 class MuduoBuffer : public BaseBuffer
 class LVProtocol : public BaseProtocol   // 接收请求时用来对message进行解析和获得对应业务层的具象BaseMessage
                                          // 接收response时，用serialize对对应业务层的具象BaseMessage进行LV格式序列化
class MuduoConnection : public BaseConnection//方便服务器管理用户连接
class MuduoServer : public BaseServer
class MuduoClient : public BaseClient

*/

// muduo库的其实内部有自己的一套判断信息到来的逻辑，他先判断这listen_sockfd就绪就创建muduo::net::TcpConnectionPtr &conn，然后调用
// 用户设置的onConnection;如果信息到来是普通套接字信息到来就去调用对应onMessage，把就绪的conn传入
// 如果信息到来的是普通套接字信息，muduo库读取部分后判断是断开信息，就直接调用用户设置的onConnection，把就绪的conn传入
// 所以一个onConnection实现客户端的连接与断开的处理工作